package com.yesjpt.util;

import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.yesjpt.util.L;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint("/websocket/{cid}")
@Component
public class WebSocketServer {

	// concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
	private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = 
			new CopyOnWriteArraySet<WebSocketServer>();

	public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
		return webSocketSet;
	}
	//第三方，用来存放昵称
	public static Set<String> cidSet = new LinkedHashSet<String>();
	//第三方，静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
	private static int onlineCount = 0;
	
	public static Set<String> getCidSet() {
		return cidSet;
	}

	public static void setCidSet(Set<String> cidSet) {
		WebSocketServer.cidSet = cidSet;
	}

	// 与某个客户端的连接会话，需要通过它来给客户端发送数据
	private Session session;

	// 客户端声明的ID，可能不止一个
	private String cid = "";
	
	// 连接建立成功调用的方法
	@OnOpen
	public void onOpen(Session session, @PathParam("cid") String cid) {
		InetSocketAddress remoteAddress = WebsocketUtil.getRemoteAddress(session);
		addOnlineCount(); // 在线数加1
		L.i(new Throwable().getStackTrace()[0] + "连接建立成功，remoteAddress = " + remoteAddress + ", session.id = " + session.getId() + ", cid = " + cid + ", 当前在线人数为：" + getOnlineCount());
		if(cid == null) {
			L.e(new Throwable().getStackTrace()[0] + "无法取得cid，设置cid为：" + session.getId());
			cid = session.getId();
		}
		this.cid = cid;
		this.session = session;
		cidSet.add(cid);
		webSocketSet.add(this); // 加入set中
	}

	// 连接关闭调用的方法
	@OnClose
	public void onClose() {
		L.i(new Throwable().getStackTrace()[0] + ", 连接关闭，cid =  " + this.cid);
		webSocketSet.remove(this); // 从set中删除
		cidSet.remove(cid);
		subOnlineCount(); // 在线数减1
	}

	/**
	 * 收到客户端消息后调用的方法
	 *
	 * @param message 客户端发送过来的消息
	 * @param session 当前的会话，全局唯一
	 */
	@OnMessage //是用得最多的方法
	public void onMessage(String message, Session session) {
		L.i(new Throwable().getStackTrace()[0] + ", message = " + message + ", cid = " + cid + ", session.id = " + session.getId());
		if(message.equals("ping")) {
			sendTo(cid, "pong");
			return;
		}
		try {
			JSONObject o = new JSONObject();
			o.put("success", true);
			o.put("message", "服务器成功接收消息");
			//开始转发
			o = JSON.parseObject(message);
			String toCid = o.getString("toCid");
			if(!StringUtils.isEmpty(toCid)) {
				//把转发的结果返回请求转发方
				sendMessage(sendTo(toCid, message).toJSONString());
			}
		} catch (IOException e) {
			L.e(new Throwable().getStackTrace()[0] + "，异常：" + e);
			JSONObject o = new JSONObject();
			o.put("success", false);
			o.put("message", "消息发送异常：" + e);
		}
	}

	/**
	 * 发生错误时调用的方法
	 * @param session
	 * @param error
	 */
	@OnError
	public void onError(Session session, Throwable error) {
		L.i(new Throwable().getStackTrace()[0] + "session.id = " + session.getId() + "，发生错误：\n" + error);
	}

	// 实现服务器立刻主动推送到当前连接过来的客户端
	public void sendMessage(String message) throws IOException {
		//L.i(new Throwable().getStackTrace()[0] + ", toCid = " + this.cid);
		//this.session.getBasicRemote().sendObject(data);
		this.session.getBasicRemote().sendText(message);
	}
	
	// 群发消息到所有客户端
	public static void broadcast(String message) throws IOException {
		L.i(new Throwable().getStackTrace()[0] + "正在群发消息：" + message);
		for (WebSocketServer item : webSocketSet) {
			try {
				L.i("正在推送到：cid = " + item.cid + ", session.id = " + item.session.getId());
				item.sendMessage(message);
			} catch (IOException e) {
				continue;
			}
		}
	}
	
	/**
	 * 发送一个消息到指定的客户端
	 * @param message 消息
	 * @param toCid 客户端ID，可能不止一个，有可能同一个cid在多处登录
	 */
	public static JSONObject sendTo(@PathParam("toCid")String toCid, String message) {
		L.i(new Throwable().getStackTrace()[0] + ", toCid = " + toCid + ", message = " + message);
		//发送成功的个数，因为会有多个客户端用同一cid的情况
		int coun = 0;
		JSONObject o = new JSONObject();
		o.put("success", false);
		o.put("message", "发送的对象【" + toCid + "】尚未连接");
		for (WebSocketServer item : webSocketSet) {
			try {
				//L.i("toCid = " + toCid + ", item.cid = " + item.cid);
				if (item.cid.equals(toCid)) {
					coun++;
					o.put("success", true);
					o.put("message", "成功地把消息【" + message + "】发送到【" + toCid + "】");
					item.sendMessage(message);
				}
			} catch (IOException e) {
				L.e(new Throwable().getStackTrace()[0] + "异常：" + e);
				continue;
			}
		}
		o.put("coun", coun);
		//L.i("发送结果：" + o);
		return o;
	}

	/*
	 * 取得在线会话个数
	 */
	public static synchronized int getOnlineCount() {
		return onlineCount;
	}
	
	/*
	 * 增加一个会话
	 */
	public static synchronized void addOnlineCount() {
		WebSocketServer.onlineCount++;
	}

	/*
	 * 减少一个会话
	 */
	public static synchronized void subOnlineCount() {
		WebSocketServer.onlineCount--;
	}

}
